🧩 一、小吴帮你理解题意
首先,我们来提炼题目核心信息:
有 n 个工厂围成一个环,编号为 1~n。
每段马路(n 条)在每个单位时间内会产出一定数量的金币。
小新只能在某个工厂购买机器人,机器人会顺时针走 k 步(1 ≤ k ≤ p),每走一步收集当前马路的金币。
每个单位时间必须有一个机器人在行走,且不能同时存在多个机器人。
每次购买机器人需要花费一定金币(每个工厂花费不同)。
游戏持续 m 个单位时间,求最终能收集到的最大金币数。
🧠 二、小吴帮你解决核心问题抽象
我们可以把问题转化为:
在 m 个时间点中,每一步都必须安排一个机器人行走 k 步(k ∈ [1, p]),且机器人起点可以任意选择,求总金币收益最大值(金币 = 收集的金币 - 购买费用)
🎯 三、此问题的关键点🗡
1. 机器人行走的收益如何计算?
如果一个机器人从工厂 i 出发,行走 k 步,则它会经过的马路编号为:
i(第1步)
(i+1)%n(第2步)
...
(i+k-1)%n(第k步)
注意:这里马路编号是按顺时针排列的。
同时,在第 t 个单位时间,机器人行走时,每一步都会对应一个时间点 t,所以我们需要知道:
马路 j 在时间 t 的金币值(输入中给出)
因此,我们可以预处理一个数组 gain[i][k][t] 表示:在时间 t,从工厂 i 出发走 k 步的总金币收益(包括收集的金币和花费)。
2. 如何表示状态?
这是一个典型的阶段决策问题,可以用动态规划解决。
我们定义状态:
dp[t] 表示在第 t 个单位时间结束时,所能获得的最大总收益。
转移方式是:枚举上一次机器人行走的结束时间,假设上一次机器人是在第 t-k 个时间点结束的,那么我们可以从 dp[t-k] 转移而来,加上这次机器人行走的净收益。
这样我们可以写出一个一维动态规划的状态转移方程。
🧮 四、小吴帮你预处理与状态转移思路
1. 预处理每种行走方式的净收益
我们可以预先计算出:
profit[i][k]:从,走 k 步(k ∈ [1,p]),可以获得的净收益
收集的金币:根据时间 t,对应马路上的金币值
减去购买费用:工厂 i 的花费
2. 动态规划状态设计
我们定义:
dp[t]:前 t 个单位时间内,获得的最大净收益
初始条件:dp[0] = 0(没有时间,没有收益)
转移方式:
对于每个 t,枚举 k ∈ [1, p],且 t - k ≥ 0
枚举起点 i ∈ [1, n]
dp[t] = max(dp[t], dp[t-k] + profit[i][k][t-k+1...t])
注意:这里的 profit[i][k] 应该是根据具体时间 t-k+1 到 t 的情况来计算。
📌 五、小吴帮你启发式思考题(你可以尝试思考以下问题)
为什么不能直接枚举所有可能的机器人行走方式?
时间复杂度是多少?为什么不能暴力?
如何高效预处理每个机器人行走的收益?
可以使用前缀和吗?如何表示每个时间点的收益?
动态规划的转移方式是否可以优化?
是否可以使用滑动窗口或滚动数组优化空间?
如何处理“环形结构”?
有没有办法把它变成“线性结构”处理?
如果 n 和 m 都是 1000,那算法的时间复杂度应该控制在多少以内?
你能设计出 O(nmp) 的算法吗?
📚 六、小吴给你相关知识点引导
你可以复习以下知识点,来更好地理解本题:
知识点 相关内容
动态规划 状态定义、状态转移、初始化、滚动数组优化
前缀和 快速计算一段区间内的总和
枚举与剪枝 控制时间复杂度,避免暴力
环形结构处理 把环形结构“断环成链”处理
决策优化 在多个选择中做出最优决策
✅ 七、小结与下一步建议
你现在需要完成以下几个步骤:
读取输入数据,并构建一个二维数组记录每条马路在每个时间点的金币数量。
预处理每个机器人行走的净收益(考虑起点、行走步数、时间)。
设计动态规划状态和转移方式,逐步计算每个时间点的最大收益。
输出最终结果:dp[m]
你可以先尝试用小样例模拟整个过程,比如题目给出的样例:
2 3 2
1 2 3
2 3 4
1 2
试着手动模拟一下每一步的决策和收益变化,这将极大帮助你理解整个算法流程。
8.小吴答案
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1010;
int n, m, p;
int s[N][N], cost[N];
int f[N], g[N][N];
struct Node
{
int v, i, j;
bool operator < (const Node & W) const
{
return v < W.v;
}
};
priority_queue<Node> heap[N];
int get(int x)
{
x %= n;
if (x <= 0)
{
x += n;
}
return x;
}
int main(void)
{
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%d", &s[i][j]);
}
}
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (j == 1)
{
s[j][i] += s[n][i - 1];
}
else
{
s[j][i] += s[j - 1][i - 1];
}
}
}
memset(f, -0x3f, sizeof f);
memset(g, -0x3f, sizeof g);
f[0] = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &cost[i]);
}
for (int i = 1; i <= n; i++)
{
g[0][i] = -cost[get(i + 1)];
heap[get(0 - i)].push({ g[0][i], 0, i });
}
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
auto& h = heap[get(i - j)];
while (h.size())
{
auto t = h.top();
if (i - p > t.i)
{
h.pop();
}
else
{
f[i] = max(f[i], s[j][i] + t.v);
break;
}
}
}
for (int j = 1; j <= n; j++)
{
g[i][j] = f[i] - s[j][i] - cost[get(j + 1)];
heap[get(i - j)].push({ g[i][j], i, j });
}
}
int res = 0;
for (int i = 0; i <= m; i++)
{
res = max(res, f[i]);
}
cout << res << endl;
return 0;
}